#ifndef GEDGEE_COMMON_H
#define GEDGEE_COMMON_H

#include <cstring>
#include <cstdint>

// compiler

#if defined(_MSC_VER)
    #define GEDGEE_COMPILER_MSVC
#elif defined(__GNUC__)
    #define GEDGEE_COMPILER_GCC
#elif defined(__clang__)
    #define GEDGEE_COMPILER_CLANG
#else
    #warning "Gedgee found unknow complier."
    #define GEDGEE_COMPILER_UNKNOW
#endif

// os

#if defined(_WIN32) || defined(_WIN64)
    #define GEDGEE_OS_WINDOWS
#elif defined(__linux__)
    #define GEDGEE_OS_LINUX
#elif defined(__APPLE__)
    #define GEDGEE_OS_MAC
#else
    #warning "Gedgee found unknow os."
    #define GEDGEE_OS_UNKNOW
#endif

// arch

#if defined(__x86_64__)
    #define GEDGEE_ARCH_x64
    #define GEDGEE_ARCH_X64
    #define GEDGEE_ARCH_x86_64
    #define GEDGEE_ARCH_X86_64
#elif defined(__i386__)
    #define GEDGEE_ARCH_x86
    #define GEDGEE_ARCH_X86
#elif defined(__arm__)
    #define GEDGEE_ARCH_ARM
#elif defined(__mips__)
    #define GEDGEE_ARCH_MIPS
#else
    #warning "Gedgee found unknow arch."
    #define GEDGEE_ARCH_UNKNOW
#endif

// unroll

#if defined(GEDGEE_COMPILER_GCC) || defined(GEDGEE_COMPILER_CLANG)
    #define GEDGEE_UNROLL _Pragma("unroll")
#else
    #define GEDGEE_UNROLL
#endif

// precision

// for GEDGEE_PRECISION
// case 0: origin precision from SIMD
// case 1: precision improve
// case 2: the same as only-c codes

#ifdef GEDGEE_PRECISION_LOW
    #define GEDGEE_PRECISION 0
#endif

#ifdef GEDGEE_PRECISION_MID
    #define GEDGEE_PRECISION 1
#endif

#ifdef GEDGEE_PRECISION_HIGH
    #define GEDGEE_PRECISION 2
#endif

#ifndef GEDGEE_PRECISION
    #define GEDGEE_PRECISION_LOW
    #define GEDGEE_PRECISION 0
#endif

namespace gedgee {
    // typedef

    typedef uint8_t u8;
    typedef uint16_t u16;
    typedef uint32_t u32;
    typedef uint64_t u64;
    typedef int8_t i8;
    typedef int16_t i16;
    typedef int32_t i32;
    typedef int64_t i64;
    typedef float f32;
    typedef double f64;

    // constexpr工具集
    namespace _tool {

        inline constexpr bool str_equal(const char *s1, const char *s2) {
            if (s1 == nullptr || s2 == nullptr) {
                return false;
            }

            for (; (*s1) && (*s2); s1 ++, s2 ++) {
                if (*s1 != *s2) {
                    return false;
                }
            }

            return true;
        }

        template <typename T1, typename T2>
        inline constexpr bool size_equal() {
            return sizeof(T1) == sizeof(T2);
        }

        template <typename T>
        inline constexpr T max(const T a, const T b) {
            if constexpr (a >= b) {
                return a;
            }
            return b;
        }

        template <typename T>
        inline constexpr T min(const T a, const T b) {
            if constexpr (a <= b) {
                return a;
            }
            return b;
        }

        template <typename T>
        inline constexpr bool is_u8() {
            return false;
        }

        template <>
        inline constexpr bool is_u8<uint8_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_u16() {
            return false;
        }

        template <>
        inline constexpr bool is_u16<uint16_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_u32() {
            return false;
        }

        template <>
        inline constexpr bool is_u32<uint32_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_u64() {
            return false;
        }

        template <>
        inline constexpr bool is_u64<uint64_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_i8() {
            return false;
        }

        template <>
        inline constexpr bool is_i8<int8_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_i16() {
            return false;
        }

        template <>
        inline constexpr bool is_i16<int16_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_i32() {
            return false;
        }

        template <>
        inline constexpr bool is_i32<int32_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_i64() {
            return false;
        }

        template <>
        inline constexpr bool is_i64<int64_t>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_f32() {
            return false;
        }

        template <>
        inline constexpr bool is_f32<float>() {
            return true;
        }

        template <typename T>
        inline constexpr bool is_f64() {
            return false;
        }

        template <>
        inline constexpr bool is_f64<double>() {
            return true;
        }

        // 判断类型是否是无符号整形(uint8_t、uint16_t、uint32_t、uint64_t)
        template <typename T>
        inline constexpr bool is_u() {
            if constexpr (is_u8<T>() || is_u16<T>() || is_u32<T>() || is_u64<T>()) {
                return true;
            }
            return false;
        }

        // 判断类型是否是有符号整形(int8_t、int16_t、int32_t、int64_t)
        template <typename T>
        inline constexpr bool is_i() {
            if constexpr (is_i8<T>() || is_i16<T>() || is_i32<T>() || is_i64<T>()) {
                return true;
            }
            return false;
        }

        // 判断类型是否是浮点型(float、double)
        template <typename T>
        inline constexpr bool is_f() {
            if constexpr (is_f32<T>() || is_f64<T>()) {
                return true;
            }
            return false;
        }
    }

    struct alignas(16) vector128;
    struct alignas(32) vector256;
    struct alignas(64) vector512;

    struct vector128 {
        union {
            uint8_t u8[16];
            uint16_t u16[8];
            uint32_t u32[4];
            uint64_t u64[2];

            int8_t i8[16];
            int16_t i16[8];
            int32_t i32[4];
            int64_t i64[2];

            float f32[4];
            double f64[2];
        };
    };

    struct vector256 {
        union {
            uint8_t u8[32];
            uint16_t u16[16];
            uint32_t u32[8];
            uint64_t u64[4];

            int8_t i8[32];
            int16_t i16[16];
            int32_t i32[8];
            int64_t i64[4];

            float f32[8];
            double f64[4];

            vector128 v128[2];
        };
    };

    struct vector512 {
        union {
            uint8_t u8[64];
            uint16_t u16[32];
            uint32_t u32[16];
            uint64_t u64[8];

            int8_t i8[64];
            int16_t i16[32];
            int32_t i32[16];
            int64_t i64[8];

            float f32[16];
            double f64[8];

            vector256 v256[2];
        };
    };

    template <typename srcT, typename dstT>
    inline vector128 v128_cvt(vector128 vec);

    template <typename T>
    inline vector128 v128_load(const T *p);

    template <typename T>
    inline void v128_store(T *p, vector128 vec);

    template <typename T>
    inline vector128 v128_set(const T n);

    template <typename T>
    inline vector128 v128_fill(const T n);

    template <typename T>
    inline T v128_get(vector128 vec);

    template <typename T>
    inline vector128 v128_add(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_sub(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_mul(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_div(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_max(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_min(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_and(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_or(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_xor(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_andnot(vector128 vec1, vector128 vec2);

    template <typename T>
    inline vector128 v128_sll(vector128 vec, uint32_t count);

    template <typename T>
    inline vector128 v128_srl(vector128 vec, uint32_t count);

    template <typename T>
    inline vector128 v128_sra(vector128 vec, uint32_t count);

    template <typename T>
    inline vector128 v128_abs(vector128 vec);

    template <typename T>
    inline vector128 v128_neg(vector128 vec);

    template <typename T>
    inline vector128 v128_sqrt(vector128 vec);

    template <typename T>
    inline vector128 v128_rsqrt(vector128 vec);

    template <typename T>
    inline vector128 v128_rcp(vector128 vec);

    template <typename T>
    inline vector128 v128_fma(vector128 vec, vector128 vmul, vector128 vadd);

    template <typename T>
    inline vector128 v128_round(vector128 vec);
}

#endif
